package com.flyxiaozhu.erp.admin.aspect;

import com.flyxiaozhu.erp.admin.authorize.AuthorizeHelper;
import com.flyxiaozhu.erp.core.entities.AdminUser;
import com.flyxiaozhu.erp.core.entities.Permission;
import com.flyxiaozhu.erp.core.entities.SysLog;
import com.flyxiaozhu.erp.core.libs.Constants;
import com.flyxiaozhu.erp.core.repositories.SysLogRepository;
import com.flyxiaozhu.erp.core.utils.LoggerUtils;
import com.flyxiaozhu.erp.core.utils.ObjectMapUtils;
import lombok.extern.slf4j.Slf4j;
import org.hibernate.Session;
import org.hibernate.event.internal.DefaultLoadEventListener;
import org.hibernate.event.service.spi.EventListenerRegistry;
import org.hibernate.event.spi.*;
import org.hibernate.internal.SessionFactoryImpl;
import org.hibernate.persister.entity.EntityPersister;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Async;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
import javax.persistence.EntityManagerFactory;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.Serializable;
import java.util.HashMap;
import java.util.Map;

@Component
@Slf4j
public class OperaListener extends DefaultLoadEventListener implements PostDeleteEventListener, PostInsertEventListener, PostUpdateEventListener {

    @Autowired
    private EntityManagerFactory entityManagerFactory;

    @Autowired(required = false)
    private HttpServletRequest request;

    @Autowired(required = false)
    private HttpServletResponse response;

    @Autowired
    private SysLogRepository sysLogRepository;

    /**
     * 注册事件
     */
    @PostConstruct
    public void registerListeners() {
        SessionFactoryImpl sessionFactoryImpl = entityManagerFactory.unwrap(SessionFactoryImpl.class);
        EventListenerRegistry eventListenerRegistry = sessionFactoryImpl.getServiceRegistry().getService(EventListenerRegistry.class);
        eventListenerRegistry.getEventListenerGroup(EventType.POST_INSERT).appendListener(this);
        eventListenerRegistry.getEventListenerGroup(EventType.POST_UPDATE).appendListener(this);
        eventListenerRegistry.getEventListenerGroup(EventType.POST_DELETE).appendListener(this);
    }


    /**
     * 监听删除事件
     * event
     */
    @Override
    @Async
    public void onPostDelete(PostDeleteEvent event) {
        // TODO 删除会抛出NULL异常
        try {
            log.debug("数据删除监听");

            Map<String, Object> change = new HashMap<>();
            // 获得实体名称
            for (int i = 0; i < event.getDeletedState().length; i++) {
                // 过滤掉创建时间字段
                if (event.getPersister().getPropertyNames()[i].equals("createTime") || event.getPersister().getPropertyNames()[i].equals("updateTime")) {
                    continue;
                }
                change.put(event.getPersister().getPropertyNames()[i], event.getDeletedState()[i]);
            }
            String remark = ObjectMapUtils.mapToJson(change);

            saveOperaLog(event.getSession(), event.getEntity().getClass().getSimpleName(), event.getId(), remark, Constants.SYS_OPERA_DELETE);
        } catch (Exception e) {
            log.debug("数据删除监听发生异常：" + e.getMessage());
        }
    }

    /**
     * 监听插入事件
     *
     * @param event
     */
    @Override
    @Async
    public void onPostInsert(PostInsertEvent event) {
        try {
            if ((event.getEntity() instanceof SysLog) || (event.getEntity() instanceof Permission)) {// 当对日志表进行插入时，不进行记录
                return;
            }

            log.debug("数据插入监听");

            Map<String, Object> change = new HashMap<>();
            for (int i = 0; i < event.getState().length; i++) {
                // 过滤掉创建时间字段
                if (event.getPersister().getPropertyNames()[i].equals("createTime") || event.getPersister().getPropertyNames()[i].equals("updateTime")) {
                    continue;
                }
                change.put(event.getPersister().getPropertyNames()[i], event.getState()[i]);
            }
            String remark = ObjectMapUtils.mapToJson(change);

            saveOperaLog(event.getSession(), event.getEntity().getClass().getSimpleName(), event.getId(), remark, Constants.SYS_OPERA_INSERT);
        } catch (Exception e) {
            log.debug("数据插入监听发生异常：" + e.getMessage());
        }
    }

    /**
     * 监听更新事件
     *
     * @param event
     */
    @Override
    @Async
    public void onPostUpdate(PostUpdateEvent event) {
        try {
            boolean temp = event.getState().equals(event.getOldState());
            // 判断两个对象的内容是否相等

            log.debug("数据更新监听");

            // 获得实体名称
            String diff = arrayDiff(event.getState(), event.getOldState(), event.getPersister().getPropertyNames());//判断修改了哪些部分，并拼接成字符串
            saveOperaLog(event.getSession(), event.getEntity().getClass().getSimpleName(), event.getId(), diff, Constants.SYS_OPERA_UPDATE);
        } catch (Exception e) {
            log.debug("数据更新监听发生异常：" + e.getMessage());
        }
    }

    /**
     * 日志的添加
     *
     * @param session
     * @param des
     */
    private void saveOperaLog(Session session, String targetEntity, Serializable targetId, String des, int methodType) {
        log.debug("数据：" + des);

        if (SecurityContextHolder.getContext().getAuthentication() == null) {
            return;
        }

        // 创建日志实体
        SysLog sysLog = new SysLog();
        // 获取用户名
        AdminUser user = AuthorizeHelper.getUser();
        sysLog.setUsername(user.getUsername());
        // 实体名称
        sysLog.setEntity(targetEntity);
        // 设置数据ID值
        sysLog.setDataId(Long.parseLong(targetId.toString()));
        // 设置方法类型
        sysLog.setMethodType(methodType);
        // 设置请求方式（post|get）
        sysLog.setRequestMethod(request.getMethod());
        // 设置请求参数
        sysLog.setRequestParams(ObjectMapUtils.mapToJson(request.getParameterMap()));
        // 设置请求地址
        sysLog.setRequestUrl(request.getRequestURI());
        // 请求者ip
        sysLog.setRequestIp(LoggerUtils.getClientIp(request));
        // 获取请求状态码
        String status = String.valueOf(response.getStatus());
        // 设置状态码
        sysLog.setRequestCode(status);
        // 设置数据变化
        sysLog.setMethodDescription(des);

        //执行将日志写入数据库
        sysLogRepository.save(sysLog);
    }

    /**
     * 数组不同部分转字符串
     *
     * @param n     成员新值
     * @param o     成员原值
     * @param names 成员名
     * @return
     */
    private String arrayDiff(Object[] n, Object[] o, String[] names) throws Exception {
        StringBuffer result = new StringBuffer();

        Map<String, Object> change = new HashMap<>();

        //各参数数组均按序传进来的，按index取值即可
        for (int i = 0; i < n.length; i++) {
            // 去除createTime字段
            if (names[i].equals("createTime") || names[i].equals("updateTime")) {
                continue;
            }
            //如不相等，则加入字符串中
            if (!String.valueOf(n[i]).equals(String.valueOf(o[i]))) {
                change.put(names[i], o[i] + ">>" + n[i]);
            }
        }
        if (change.isEmpty()) {
            change.put("data", "内容没发生变化，但内容地址改变");
        }

        return ObjectMapUtils.mapToJson(change);
    }

    @Override
    public boolean requiresPostCommitHanding(EntityPersister entityPersister) {
        return false;
    }

}
